%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Module summary
%%
%% Main authors: Peter
%% Progress to usefulness: 
%%
%% Ets worker, dynamically added by ets_master.
%% Performs the storing of data.
%% 
%% External function calls:
%% 
%% {put, Key, Value} -- Store 'Key' and 'Value' at correct node
%% {get, Key}        -- Get value from node holding 'Key'
%%
%%%%%%%%%%%%%%%%%%%%%%%
-module(ets_worker).
-behaviour(gen_server).
-include("db.hrl").

% gen_server API
-export([start_link/0,start_link/1]).

% gen_server API
-export([init/1, handle_call/3,handle_cast/2,handle_info/2
        ,terminate/2, code_change/3]).


start_link() ->
    %io:format("Starting Ets worker\n"),
    gen_server:start_link(?MODULE,[],[]).

start_link(Args) ->
    %io:format("Starting Ets worker\n"),
    gen_server:start_link(?MODULE,Args,[]).

% Initial dictionary
% should return {ok, State}
init(State) ->
    {ok, State}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%   Internal "function calls"
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%
% Handle put
%
handle_cast({From, {put, Key, Value}}, State) ->
    ets:insert(State#ets_worker.table, {Key, Value}),
    case State#ets_worker.verbose of
        true  ->
            io:format("Inserting: (~p,~p)~n",[Key,Value]);
        false -> ok
    end,
    Succs = gen_server:call(?NETWORK, {successors}),
    [gen_server:cast({?BACKEND,Node}, {backup,Key,Value}) || Node <- Succs],
    gen_server:reply(From, {ok,put}),
    {stop, normal, State};

% Handle get
%
handle_cast({From, {get, Key}}, State) ->
    Result = ets:lookup(State#ets_worker.table, Key),
    case length(Result) == 0 of
        true  -> Reply = error;
        false -> Reply = lists:nth(1,Result)
    end,
    gen_server:reply(From, Reply),
    {stop, normal,  State};


%
% This node want to leave chord network, send values to the new responsible node.
%
handle_cast({From, {leave, Node}}, State) ->
    %% safe_fixtable -> fixes table for safe traversal
    ets:safe_fixtable(State#ets_worker.table,true),
    % Traverse the table and send all values to 'Node'
    ets:foldr(fun({Key, Value},Acc) ->
                   gen_server:call({?BACKEND,Node}, {put, Key, Value}),
                   Acc end,notused,State#ets_worker.table),
    % And delete all object in table
    ets:delete_all_objects(State#ets_worker.table),
    ets:safe_fixtable(State#ets_worker.table,false),
    gen_server:reply(From, ok),
    {stop, normal, State};

%
% Status, returns the current state (#values)
%
handle_cast({From, {status}}, State) ->
    Size = element(2,(lists:nth(6,ets:info(State#ets_worker.table)))),
    Fmt = "#values: ~p~n",
    Params = [Size],
    gen_server:reply(From, {Fmt, Params}),
    {stop, normal, State};

handle_cast({From, {status_full}}, State) ->
    Size = element(2,(lists:nth(6,ets:info(State#ets_worker.table)))),
    Fmt = 
"#values: ~p
table: ~p
verbose: ~p
",
    Params = [Size, State#ets_worker.table,
              State#ets_worker.verbose],
    gen_server:reply(From, {Fmt, Params}),
    {stop, normal, State};

%
% Split the current values between this node and 
% the joining node.
%
handle_cast({From, {join}}, State) ->
    ets:safe_fixtable(State#ets_worker.table,true),
    ets:foldr(fun({Key, Value},Acc) ->
                Dest = gen_server:call(?NETWORK,{lookup, Key}),
                case Dest == node() of
                    false ->
                        gen_server:call({?BACKEND,Dest}, {put, Key, Value}),
                        visual:visual_element(Key,Value,Dest,[]),
                        ets:delete(State#ets_worker.table,Key);
                        % TODO: Visualize delete element from node
                    true  -> 
                        ok
                end, 
                Acc end,notused,State#ets_worker.table),
    ets:safe_fixtable(State#ets_worker.table,false),
    gen_server:reply(From, ok),
    {stop, normal, State};

% Redundancy
handle_cast({backup, Key, Value}, State) ->
    ets:insert(State#ets_worker.table, {Key, Value}),
    case State#ets_worker.verbose of
        true  ->
            io:format("Inserting: (~p,~p)~n",[Key,Value]);
        false -> ok
    end,
    {noreply, State};

handle_cast({update_backup}, State) ->
    ets:safe_fixtable(State#ets_worker.table,true),
    ets:foldr(fun({Key, Value},Acc) ->
                Dest = gen_server:call(?NETWORK,{lookup, Key}),
                case Dest == node() of
                    true  ->
                        Succs = gen_server:call(?NETWORK, {successors}),
                        [gen_server:cast({?BACKEND,Node}, {backup,Key,Value}) 
                        || Node <- Succs, Node /= node()];
                    false -> 
                        ok
                end, 
                Acc end,notused,State#ets_worker.table),
    ets:safe_fixtable(State#ets_worker.table,false),
    {noreply, State}.

% Not used
handle_call(_Msg, _From, State) ->
    {noreply, State}.

terminate(normal, _State) ->
    ok;
terminate(_Reason,_State) ->
    io:format("Terminating ets worker~n").


handle_info(Info, State) ->
    io:format("Info message received: ~w~n",[Info]),
    {noreply, State}.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

